Highlight code with dual-theme light-dark() colors so the first frame matches - #988
Merged
Conversation
… matches The shiki theme was picked in JS (useIsDark → matchMedia), which is wrong for the SSR'd first frame: the server doesn't know the scheme, so a dark-mode visitor's connect-card command rendered light-theme token colors until hydration re-resolved the theme. Dual-theme mode bakes both palettes into the markup as light-dark(...) colors and the browser's own color scheme picks one — identical computed colors at first paint and after settle, in both schemes, and a scheme flip needs no re-render. The streamdown plugin's token cache also stops being keyed (and poisoned) by whichever scheme computed first.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-marketing | b5b79e4 | Commit Preview URL Branch Preview URL |
Jun 12 2026, 08:39 PM |
Contributor
Cloudflare previewTorn down — the PR is closed. |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-cloud | b5b79e4 | Jun 12 2026, 08:40 PM |
@executor-js/cli
@executor-js/config
@executor-js/execution
@executor-js/sdk
@executor-js/codemode-core
@executor-js/runtime-quickjs
@executor-js/plugin-file-secrets
@executor-js/plugin-graphql
@executor-js/plugin-keychain
@executor-js/plugin-mcp
@executor-js/plugin-onepassword
@executor-js/plugin-openapi
executor
commit: |
RhysSullivan
marked this pull request as ready for review
June 12, 2026 20:44
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Code blocks resolved their shiki theme in JavaScript (
useResolvedShikiTheme→useIsDark→matchMedia). During SSR there is nowindow, so the server always rendered light-theme token colors; a dark-mode visitor saw the wrong palette on the first frame (visible on the connect card'snpx add-mcpcommand) until hydration re-resolved the theme and the colors snapped. The streamdown highlighter plugin had a worse variant: its token cache was keyed by whichever color scheme computed first, so a stale palette could stick for the session.Change
Switch all three highlight call sites to shiki's dual-theme mode with
defaultColor: "light-dark()". Both palettes are baked into the markup as CSSlight-dark(#light, #dark)colors and the browser's own color scheme picks one:CodeBlock's tree and the streamdown plugin pick the colors up automatically;ExpandableCodeBlock's custom token renderer now readstoken.htmlStyle(where dual-theme colors live) instead of the single-themetoken.color. Thethemeprop still works, now meaning "this theme in both schemes".useResolvedShikiThemeis deleted along withuseIsDark's last consumer.Verification
Against a live stack, signed in via the emulator flow, with
/api/account/meheld open so the first SSR'd frame is inspectable: computed span colors of the connect-card command compared at first paint vs after settle, in bothcolorScheme: darkandlightbrowser contexts — byte-identical in both (previously the dark context's first frame showed the light palette). Screenshots of both schemes' first frames confirm visually. Format, lint, and typecheck pass.Note: the inline
styleattribute string still changes at hydration (React re-serializes hex to rgb); the computed colors — what's painted — do not.